{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "from numba import njit, cuda\n",
    "import itertools\n",
    "from multiprocessing import Pool, cpu_count\n",
    "import random\n",
    "\n",
    "fam = pd.read_csv('family_data.csv')\n",
    "sub = pd.read_csv('submission_69850.59974878246.csv')\n",
    "fam = pd.merge(fam,sub, how='left', on='family_id')\n",
    "choices = fam[['choice_'+str(i) for i in range(10)]].values\n",
    "fam = fam[['n_people','assigned_day']].values\n",
    "best_fam= fam.copy()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "fam_costs = np.zeros((5000,101))\n",
    "for f in range(5000):\n",
    "    for d in range(1,101):\n",
    "        l = list(choices[f])\n",
    "        if d in l:\n",
    "            if l.index(d) == 0:\n",
    "                fam_costs[f,d] = 0\n",
    "            elif l.index(d) == 1:\n",
    "                fam_costs[f,d] = 50\n",
    "            elif l.index(d) == 2:\n",
    "                fam_costs[f,d] = 50 + 9 * fam[f,0]\n",
    "            elif l.index(d) == 3:\n",
    "                fam_costs[f,d] = 100 + 9 * fam[f,0]\n",
    "            elif l.index(d) == 4:\n",
    "                fam_costs[f,d] = 200 + 9 * fam[f,0]\n",
    "            elif l.index(d) == 5:\n",
    "                fam_costs[f,d] = 200 + 18 * fam[f,0]\n",
    "            elif l.index(d) == 6:\n",
    "                fam_costs[f,d] = 300 + 18 * fam[f,0]\n",
    "            elif l.index(d) == 7:\n",
    "                fam_costs[f,d] = 300 + 36 * fam[f,0]\n",
    "            elif l.index(d) == 8:\n",
    "                fam_costs[f,d] = 400 + 36 * fam[f,0]\n",
    "            elif l.index(d) == 9:\n",
    "                fam_costs[f,d] = 500 + 235 * fam[f,0]\n",
    "        else:\n",
    "            fam_costs[f,d] = 500 + 434 * fam[f,0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit(fastmath=True)\n",
    "def cost_function(pred):\n",
    "    days = np.array(list(range(100,0,-1)))\n",
    "    daily_occupancy = np.zeros(101)\n",
    "    penalty = 0\n",
    "    for i in range(5000):\n",
    "        penalty += fam_costs[i,pred[i,1]]\n",
    "        daily_occupancy[pred[i,1]] += pred[i,0]\n",
    "\n",
    "    for v in daily_occupancy[1:]:\n",
    "        if (v < 125) or (v >300):\n",
    "            penalty += 100000000\n",
    "                \n",
    "    accounting_cost = (daily_occupancy[days[0]]-125.0) / 400.0 * daily_occupancy[days[0]]**(0.5)\n",
    "    # using the max function because the soft constraints might allow occupancy to dip below 125\n",
    "    accounting_cost = max(0, accounting_cost)\n",
    "    \n",
    "    yesterday_count = daily_occupancy[days[0]]\n",
    "    for day in days[1:]:\n",
    "        today_count = daily_occupancy[day]\n",
    "        diff = abs(today_count - yesterday_count)\n",
    "        accounting_cost += max(0, (daily_occupancy[day]-125.0) / 400.0 * daily_occupancy[day]**(0.5 + diff / 50.0))\n",
    "        yesterday_count = today_count\n",
    "    penalty += accounting_cost\n",
    "\n",
    "    return penalty\n",
    "                \n",
    "                \n",
    "                \n",
    "                \n",
    "@njit(fastmath=True)\n",
    "def cost_function_relax(pred, constraint_penalty= 500):\n",
    "    days = np.array(list(range(100,0,-1)))\n",
    "    daily_occupancy = np.zeros(101)\n",
    "    penalty = 0\n",
    "    for i in range(5000):\n",
    "        penalty += fam_costs[i,pred[i,1]]\n",
    "        daily_occupancy[pred[i,1]] += pred[i,0]\n",
    "\n",
    "    for v in daily_occupancy[1:]:\n",
    "        if (v < 125) or (v >300):\n",
    "            if v > 300:\n",
    "                penalty += abs(v-300)*constraint_penalty\n",
    "            else:\n",
    "                penalty += abs(v-125)*constraint_penalty\n",
    "                \n",
    "\n",
    "    accounting_cost = (daily_occupancy[days[0]]-125.0) / 400.0 * daily_occupancy[days[0]]**(0.5)\n",
    "    # using the max function because the soft constraints might allow occupancy to dip below 125\n",
    "    accounting_cost = max(0, accounting_cost)\n",
    "    \n",
    "    yesterday_count = daily_occupancy[days[0]]\n",
    "    for day in days[1:]:\n",
    "        today_count = daily_occupancy[day]\n",
    "        diff = abs(today_count - yesterday_count)\n",
    "        accounting_cost += max(0, (daily_occupancy[day]-125.0) / 400.0 * daily_occupancy[day]**(0.5 + diff / 50.0))\n",
    "        yesterday_count = today_count\n",
    "    penalty += accounting_cost\n",
    "\n",
    "    return penalty"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "69850.70384985393"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "best = cost_function_relax(fam)\n",
    "best"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(5000, 101)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fam_costs.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit(fastmath=True)\n",
    "def penalty_score_(d, cp, dc):\n",
    "    penalty = 0\n",
    "    yc, tc = dc[d + 1], dc[d] + cp #current\n",
    "    penalty += max(0, (tc-125.0) / 400.0 * tc**(0.5 + abs(tc - yc) / 50.0))\n",
    "    yc, tc = dc[d] + cp, dc[d -1] #next\n",
    "    penalty += max(0, (tc-125.0) / 400.0 * tc**(0.5 + abs(tc - yc) / 50.0))\n",
    "    return penalty\n",
    "\n",
    "\n",
    "@njit(fastmath=True)\n",
    "def penalty_score(f,cd,d,cp, dc):\n",
    "    old = penalty_score_(int(cd), 0, dc) +  penalty_score_(int(d), 0, dc) + fam_costs[f][cd]\n",
    "    new = penalty_score_(int(cd), -int(cp), dc) +  penalty_score_(int(d), int(cp), dc) + fam_costs[f][d]\n",
    "    return new - old\n",
    "\n",
    "@njit(fastmath=True)\n",
    "def penalty_score2(f1,f2,d1,d2,c1,c2, dc): #single swap - can be improved\n",
    "    old = penalty_score_(int(d1), 0, dc) +  penalty_score_(int(d2), 0, dc) + fam_costs[f1][d1] + fam_costs[f2][d2]\n",
    "    new = penalty_score_(int(d1), int(c2-c1), dc) +  penalty_score_(int(d2), int(c1-c2), dc) + fam_costs[f1][d2] + fam_costs[f2][d1]\n",
    "    return new - old"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit(fastmath=True)\n",
    "def penalty_score_(d, cp, dc):\n",
    "    penalty = 0\n",
    "    yc, tc = dc[d + 1], dc[d] + cp #current\n",
    "    penalty += max(0, (tc-125.0) / 400.0 * tc**(0.5 + abs(tc - yc) / 50.0))\n",
    "    yc, tc = dc[d] + cp, dc[d -1] #next\n",
    "    penalty += max(0, (tc-125.0) / 400.0 * tc**(0.5 + abs(tc - yc) / 50.0))\n",
    "    return penalty\n",
    "\n",
    "\n",
    "@njit(fastmath=True)\n",
    "def penalty_score(f,cd,d,cp, dc):\n",
    "    old = penalty_score_(int(cd), 0, dc) +  penalty_score_(int(d), 0, dc) + fam_costs[f][cd]\n",
    "    new = penalty_score_(int(cd), -int(cp), dc) +  penalty_score_(int(d), int(cp), dc) + fam_costs[f][d]\n",
    "    return new - old\n",
    "\n",
    "@njit(fastmath=True)\n",
    "def penalty_score2(f1,f2,d1,d2,c1,c2, dc): #single swap - can be improved\n",
    "    old = penalty_score_(int(d1), 0, dc) +  penalty_score_(int(d2), 0, dc) + fam_costs[f1][d1] + fam_costs[f2][d2]\n",
    "    new = penalty_score_(int(d1), int(c2-c1), dc) +  penalty_score_(int(d2), int(c1-c2), dc) + fam_costs[f1][d2] + fam_costs[f2][d1]\n",
    "    return new - old"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "11\n",
      "[5, 33, -94]\n",
      "33\n",
      "[5, -94]\n",
      "-94\n",
      "[5]\n",
      "5\n",
      "[]\n"
     ]
    }
   ],
   "source": [
    "f_list= [5,11,33,-94]\n",
    "while f_list:\n",
    "    x = f_list.pop(random.randint(0,len(f_list)-1))\n",
    "    print(x)\n",
    "    print(f_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0., 0., 0.])"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.zeros(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4.5399929762484854e-05"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.exp(-10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit(fastmath=True)\n",
    "def optimizer(pred):\n",
    "    days = np.array(list(range(100,1,-1)))\n",
    "    days_count = np.zeros(101)\n",
    "    for i in range(5000):\n",
    "        days_count[pred[i,1]] += pred[i,0]\n",
    "        \n",
    "    f_list=list(range(5000))\n",
    "    while f_list:\n",
    "        f = f_list.pop(random.randint(0,len(f_list)-1))\n",
    "\n",
    "        cd = int(pred[f,1])   ## cd is the date of prediction for each family\n",
    "        if cd > 1 and cd < 100:\n",
    "            cp = int(pred[f,0])  ##get a family size\n",
    "            \n",
    "            d_list= list(range(2,100))\n",
    "            while d_list:\n",
    "                d = d_list.pop(random.randint(0,len(d_list)-1))\n",
    "\n",
    "                if d != cd: ##if the day is not the current prediction date\n",
    "                    # If adding or removing the family wouldn't move the new or current day into disallowed ranges \n",
    "                    if days_count[d]+cp>=125 and days_count[d]+cp<=300 and days_count[cd]-cp >= 125 and days_count[cd]-cp<=300:\n",
    "                        if penalty_score(f, int(cd), int(d), int(cp), days_count)<0:\n",
    "                            days_count[d] += cp\n",
    "                            days_count[cd] -= cp\n",
    "                            pred[f,1] = int(d)\n",
    "                            cd = int(d)\n",
    "                        elif fam_costs[f,d] <= fam_costs[f,cd]:\n",
    "                            #Get all families with new day and same number of people\n",
    "                            dtf = [fx for fx in range(5000) if ((pred[fx,1]==d) and (pred[fx,0]==cp))]\n",
    "                            while dtf:\n",
    "                                j = dtf.pop(random.randint(0,len(dtf)-1)) #look for like no move cost\n",
    "                                if j != f:\n",
    "                                    if fam_costs[f,d] + fam_costs[j,cd] <= fam_costs[f,cd] + fam_costs[j,d]:\n",
    "                                        pred[f,1] = int(d)\n",
    "                                        pred[j,1] = int(cd)\n",
    "                                        cd = int(d)\n",
    "                                        #break\n",
    "    return pred\n",
    "\n",
    "#https://www.kaggle.com/c/santa-workshop-tour-2019/discussion/119858#latest-687217\n",
    "@njit(fastmath=True)\n",
    "def optimizer_stoca(pred, annealing=5.0, seed=28, la=1.0, preference_randomness=5):\n",
    "    np.random.seed(seed)\n",
    "    days = np.array(list(range(100,1,-1)))\n",
    "    days_count = np.zeros(101)\n",
    "    for i in range(5000):\n",
    "        days_count[pred[i,1]] += pred[i,0]\n",
    "    for f in range(5000):\n",
    "        cd = int(pred[f,1])\n",
    "        if cd > 1 and cd < 100:\n",
    "            cp = int(pred[f,0])\n",
    "            for d in days[1:-1]:\n",
    "                if d != cd:\n",
    "                    if days_count[d]+cp>=125 and days_count[d]+cp<=300 and days_count[cd]-cp >= 125 and days_count[cd]-cp<=300:\n",
    "                        if penalty_score(f, int(cd), int(d), int(cp), days_count)<=0:\n",
    "                            days_count[d] += cp\n",
    "                            days_count[cd] -= cp\n",
    "                            pred[f,1] = int(d)\n",
    "                            cd = int(d)\n",
    "                        elif np.random.rand()<np.exp(-penalty_score(f, int(cd), int(d), int(cp), days_count)/np.random.randint(la,annealing)):\n",
    "                            days_count[d] += cp\n",
    "                            days_count[cd] -= cp\n",
    "                            pred[f,1] = int(d)\n",
    "                            cd = int(d)\n",
    "                        elif fam_costs[f,d] <= fam_costs[f,cd]:\n",
    "                            dtf = [fx for fx in range(5000) if ((pred[fx,1]==d) and (pred[fx,0]==cp))]\n",
    "                            for j in dtf: #like for like no move cost\n",
    "                                if j != f:\n",
    "                                    if fam_costs[f,d] + fam_costs[j,cd] <= fam_costs[f,cd] + fam_costs[j,d] + np.random.randint(preference_randomness):\n",
    "                                        pred[f,1] = int(d)\n",
    "                                        pred[j,1] = int(cd)\n",
    "                                        cd = int(d)\n",
    "                                        #break\n",
    "    return pred\n",
    "\n",
    "#https://www.kaggle.com/c/santa-workshop-tour-2019/discussion/119858#latest-687217\n",
    "@njit(fastmath=True)\n",
    "def optimizer_stoca_random_start(pred, annealing=5.0, seed=28, la=1.0, preference_randomness=5):\n",
    "    np.random.seed(seed)\n",
    "    days = np.array(list(range(100,1,-1)))\n",
    "    days_count = np.zeros(101)\n",
    "    for i in range(5000):\n",
    "        days_count[pred[i,1]] += pred[i,0]\n",
    "        \n",
    "    f_list=list(range(5000))\n",
    "    while f_list:\n",
    "        f = f_list.pop(random.randint(0,len(f_list)-1))\n",
    "        \n",
    "        cd = int(pred[f,1])\n",
    "        if cd > 1 and cd < 100:\n",
    "            cp = int(pred[f,0])\n",
    "            \n",
    "            d_list= list(range(2,100))\n",
    "            while d_list:\n",
    "                d = d_list.pop(random.randint(0,len(d_list)-1))\n",
    "                if d != cd:\n",
    "                    if days_count[d]+ cp>=125 and days_count[d]+cp<=300  and days_count[cd]-cp >= 125  and days_count[cd]-cp<=300:\n",
    "                        if penalty_score(f, int(cd), int(d), int(cp), days_count)<=0:\n",
    "                            days_count[d] += cp\n",
    "                            days_count[cd] -= cp\n",
    "                            pred[f,1] = int(d)\n",
    "                            cd = int(d)\n",
    "                        elif np.random.rand()<np.exp(-penalty_score(f, int(cd), int(d), int(cp), days_count)/np.random.randint(la,annealing)):\n",
    "#                         elif np.random.rand()<np.exp(-penalty_score(f, int(cd), int(d), int(cp), days_count)/(la+annealing*float(np.random.rand(1)))):\n",
    "                            days_count[d] += cp\n",
    "                            days_count[cd] -= cp\n",
    "                            pred[f,1] = int(d)\n",
    "                            cd = int(d)\n",
    "                        elif fam_costs[f,d] <= fam_costs[f,cd]:\n",
    "                            dtf = [fx for fx in range(5000) if ((pred[fx,1]==d) and (pred[fx,0]==cp))]\n",
    "                            \n",
    "                            while dtf: #like for like no move cost\n",
    "                                j = dtf.pop(random.randint(0,len(dtf)-1))\n",
    "                                if j != f:\n",
    "                                    if fam_costs[f,d] + fam_costs[j,cd] <= fam_costs[f,cd] + fam_costs[j,d] + np.random.randint(preference_randomness):\n",
    "                                        pred[f,1] = int(d)\n",
    "                                        pred[j,1] = int(cd)\n",
    "                                        cd = int(d)\n",
    "                                        #break \n",
    "    return pred\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "@njit(fastmath=True)\n",
    "def optimizer_a2(fam, annealing=5., seed=10, la=0.):\n",
    "    np.random.seed(seed)\n",
    "    days_count = np.zeros(101)\n",
    "    for i in range(5000):\n",
    "        days_count[fam[i,1]] += fam[i,0]\n",
    "        \n",
    "    f1_list=list(range(0,5000,1)) \n",
    "            \n",
    "#     for f1 in range(0,5000,1):\n",
    "#         for f2 in range(f1+1,5000,1):\n",
    "    while f1_list:\n",
    "        f1 = f1_list.pop(random.randint(0,len(f1_list)-1))\n",
    "        f2_list=list(range(f1+1,5000,1))\n",
    "        while f2_list:\n",
    "            f2 = f2_list.pop(random.randint(0,len(f2_list)-1))\n",
    "            \n",
    "            d1, d2 = int(fam[f1,1]), int(fam[f2,1])\n",
    "            c1, c2 = int(fam[f1,0]), int(fam[f2,0])\n",
    "            if f1 != f2 and d1 != d2 and min([d1,d2])>1 and max([d1,d2])<100:\n",
    "                if days_count[d1]+c2-c1>125 and days_count[d1]+c2-c1<300 and days_count[d2]+c1-c2 > 125 and days_count[d2]+c1-c2<300:\n",
    "                    if penalty_score2(int(f1), int(f2), int(d1), int(d2), int(c1), int(c2), days_count) <= 0 + np.random.randint(la,annealing):\n",
    "                        #print(f1,d1,c1, f2, d2,c2, penalty_score2(int(f1), int(f2), int(d1), int(d2), int(c1), int(c2), days_count))\n",
    "                        days_count[d2] += c1 - c2\n",
    "                        days_count[d1] += c2 - c1\n",
    "                        fam[f1,1] = int(d2)\n",
    "                        fam[f2,1] = int(d1)\n",
    "                        d1 = int(d2)\n",
    "                        #print(cost_function(fam))\n",
    "    return fam"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 180,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2.4606742238843857"
      ]
     },
     "execution_count": 180,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "1+2*float(np.random.rand(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [],
   "source": [
    "days = np.array(list(range(100,1,-1)))\n",
    "days_count = np.zeros(101)\n",
    "for i in range(5000):\n",
    "    days_count[fam[i,1]] += fam[i,0]\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [],
   "source": [
    "cd = int(fam[0,1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "52"
      ]
     },
     "execution_count": 59,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([  0., 300., 289., 299., 298., 277., 251., 234., 238., 264., 288.,\n",
       "       300., 296., 274., 260., 255., 268., 290., 288., 268., 244., 219.,\n",
       "       230., 256., 282., 298., 289., 270., 255., 248., 245., 274., 279.,\n",
       "       255., 223., 200., 179., 202., 234., 260., 242., 210., 182., 178.,\n",
       "       205., 242., 263., 244., 218., 193., 181., 212., 248., 242., 221.,\n",
       "       187., 158., 125., 215., 244., 229., 199., 159., 124., 124., 125.,\n",
       "       238., 211., 175., 131., 125., 125., 125., 224., 210., 178., 134.,\n",
       "       125., 126., 125., 214., 207., 175., 131., 124., 126., 125., 252.,\n",
       "       229., 197., 156., 125., 124., 124., 221., 201., 169., 125., 125.,\n",
       "       126., 126.])"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "days_count"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "500072485.50799847"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cost_function(fam)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(0, 12) 69850.70384985393 0.0\n",
      "(0, 12) 69850.70384985393 0.0\n",
      "(0, 11) 69855.76930326885 5.065453414921649\n",
      "(0, 11) 69850.70384985393 0.0\n",
      "(0, 10) 69867.75231507918 17.048465225248947\n",
      "(0, 10) 69851.41834849017 0.7144986362400232\n",
      "(0, 9) 69850.70384985393 0.0\n",
      "(0, 9) 69851.78690698967 1.0830571357364533\n",
      "(0, 8) 69851.70291455362 0.9990646996884607\n",
      "(0, 8) 69850.70384985393 0.0\n",
      "(0, 7) 69851.70291455362 0.9990646996884607\n",
      "(0, 7) 69851.41834849017 0.7144986362400232\n",
      "(1, 12) 69854.19300336765 3.4891535137139726\n",
      "(1, 12) 69850.70384985393 0.0\n",
      "(1, 11) 69855.3545514981 4.650701644161018\n",
      "(1, 11) 69850.70384985393 0.0\n",
      "(1, 10) 69860.63617532754 9.932325473608216\n",
      "(1, 10) 69850.59974878246 -0.10410107146890368\n",
      "-0.10410107146890368 yes\n",
      "(1, 9) 69859.71152553054 9.1117767480755\n",
      "(1, 9) 69850.59974878246 0.0\n",
      "(1, 8) 69850.59974878246 0.0\n",
      "(1, 8) 69850.59974878246 0.0\n",
      "(1, 7) 69853.03470167925 2.4349528967868537\n",
      "(1, 7) 69850.59974878246 0.0\n",
      "(2, 12) 69850.88431484593 0.2845660634629894\n",
      "(2, 12) 69850.59974878246 0.0\n",
      "(2, 11) 69855.25045042663 4.650701644161018\n",
      "(2, 11) 69857.10743534216 6.5076865596929565\n",
      "(2, 10) 69860.58366079733 9.98391201486811\n",
      "(2, 10) 69855.42700085773 4.827252075265278\n",
      "(2, 9) 69853.5873592952 2.9876105127332266\n",
      "(2, 9) 69852.37889885137 1.7791500689054374\n",
      "(2, 8) 69852.37889885137 1.7791500689054374\n",
      "(2, 8) 69851.21632350262 0.6165747201594058\n",
      "(2, 7) 69851.21632350262 0.6165747201594058\n",
      "(2, 7) 69851.21632350262 0.6165747201594058\n",
      "(3, 12) 69856.41481234232 5.815063559857663\n",
      "(3, 12) 69854.26442550898 3.6646767265192466\n",
      "(3, 11) 69857.98759540316 7.3878466206952\n",
      "(3, 11) 69854.22499476056 3.625245978095336\n",
      "(3, 10) 69854.14261431679 3.542865534327575\n",
      "(3, 10) 69851.21632350262 0.6165747201594058\n",
      "(3, 9) 69863.82280046541 13.22305168294406\n",
      "(3, 9) 69854.26442550898 3.6646767265192466\n",
      "(3, 8) 69855.31298171895 4.713232936483109\n",
      "(3, 8) 69851.21632350262 0.6165747201594058\n",
      "(3, 7) 69852.23792072943 1.638171946964576\n",
      "(3, 7) 69851.21632350262 0.6165747201594058\n",
      "(4, 12) 69853.36671033596 2.766961553497822\n",
      "(4, 12) 69851.21632350262 0.6165747201594058\n",
      "(4, 11) 69857.3499599378 6.750211155333091\n",
      "(4, 11) 69855.42700085773 4.827252075265278\n",
      "(4, 10) 69864.79592938315 14.196180600687512\n",
      "(4, 10) 69857.32452400321 6.724775220747688\n",
      "(4, 9) 69861.78245429289 11.182705510422238\n",
      "(4, 9) 69861.0135931232 10.413844340742799\n",
      "(4, 8) 69862.30948784089 11.70973905842402\n",
      "(4, 8) 69851.21632350262 0.6165747201594058\n",
      "(4, 7) 69856.79821617281 6.198467390349833\n",
      "(4, 7) 69852.37889885137 1.7791500689054374\n"
     ]
    }
   ],
   "source": [
    "# %%time\n",
    "# best_fam = fam.copy()\n",
    "for j in range(5):\n",
    "    for i in range(12,6,-1):\n",
    "        th = i*10\n",
    "        df = optimizer_stoca_random_start(fam, 4, i, preference_randomness=10)\n",
    "        fam=df\n",
    "        new = cost_function(df)\n",
    "        print((j, i), new, new - best)\n",
    "        if new <= best + th:\n",
    "            fam = optimizer_stoca_random_start(fam, 2, i, preference_randomness=2)\n",
    "            fam = optimizer_a2(fam, 8.0)\n",
    "            fam = optimizer(fam)\n",
    "            new = cost_function(fam)\n",
    "            print((j, i), new, new - best)\n",
    "            if new < best:\n",
    "                print(new - best, 'yes')\n",
    "                best = new\n",
    "                best_fam = fam.copy()\n",
    "\n",
    "fam = optimizer(best_fam)\n",
    "best = cost_function(fam)\n",
    "pd.DataFrame({'family_id':list(range(5000)), 'assigned_day':best_fam[:,1]}).to_csv(f'submission_{best}.csv', index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "69850.59974878246"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cost_function(fam)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "69850.59974878246"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "best"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "69850.59974878246"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cost_function(best_fam)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
